﻿#define AUTO_LOGIN
using Circus.Utility;
using System;
using System.Data;
using System.Data.SqlClient;
using System.Web;

namespace Teal.Club {

    /// <summary>
    /// 提供用户控制相关接口。
    /// </summary>
    public static class UserController {

        /// <summary>
        /// 用于保存用户 ID 的 Cookie 名。
        /// </summary>
        private const string userIdCookieName = "teal_uid";

        /// <summary>
        /// 用于保存用户名的 Cookie 名。
        /// </summary>
        private const string userNameCookieName = "teal_uname";

        /// <summary>
        /// 用于保存用户登录通信证的 Cookie 名。
        /// </summary>
        private const string userPassportCookieName = "teal_ucard";

        /// <summary>
        /// 用户相关 Cookie 的过期秒数。
        /// </summary>
        private const int cookieExpires = 60 * 60 * 24 * 30;

        /// <summary>
        /// 用于保存用户登录信息的 Cookie 所在的域。
        /// </summary>
        private const string cookieDomain = null;

        /// <summary>
        /// 用于保存用户登录信息的 Session 名。
        /// </summary>
        private const string userSessionName = "u";

        /// <summary>
        /// 存储默认的用户首选项。
        /// </summary>
        private readonly static User anymouseUser = new User() { };

        /// <summary>
        /// 获取当前用户的首选项。
        /// </summary>
        public static User currentPreference {
            get {
                return getLoginUser() ?? anymouseUser;
            }
        }

        /// <summary>
        /// 判断当前用户是否已登录。
        /// </summary>
        public static bool isLogged {
            get {
                return getLoginUser() != null;
            }
        }

        /// <summary>
        /// 获取当前已登录的用户。如果用户未登录则尝试自动登录，如果仍然无法登录则报告错误。
        /// </summary>
        public static User currentUser {
            get {
                return checkLogin();
            }
            private set {
                HttpContext.Current.Session[userSessionName] = value;
            }
        }

        /// <summary>
        /// 获取当前登录的用户，获取时尝试自动登录。
        /// </summary>
        /// <returns>返回已登录的用户。如果用户未登录则返回 null。</returns>
        private static User getLoginUser() {

            // 判断是否已登录。
            var user = (User)HttpContext.Current.Session[userSessionName];
            if (user == null) {

                // 尝试自动登录。
                var ucard = HttpContext.Current.Request.Cookies[userPassportCookieName];
                if (ucard != null) {

                    try {
                        var id = long.Parse(HttpContext.Current.Request.Cookies[userIdCookieName].Value);
                        var lastLoginTime = new DateTime(long.Parse(Encryption.decryptAES(ucard.Value)));
                        var newLoginTime = DateTime.Now;

                        var nameParam = DbHelper.createOutputParameter("@name", SqlDbType.VarChar, 32);
                        var emailParam = DbHelper.createOutputParameter("@email", SqlDbType.VarChar, 64);
                        var roleParam = DbHelper.createOutputParameter("@role", SqlDbType.TinyInt);
                        var avatarParam = DbHelper.createOutputParameter("@avatar", SqlDbType.VarChar, 256);
                        var skillsParam = DbHelper.createOutputParameter("@skills", SqlDbType.VarChar, 256);
                        var levelParam = DbHelper.createOutputParameter("@level", SqlDbType.Int);
                        var jobStateParam = DbHelper.createOutputParameter("@jobState", SqlDbType.TinyInt);
                        var workYearTypeParam = DbHelper.createOutputParameter("@workYearType", SqlDbType.TinyInt);
                        var jobTypeParam = DbHelper.createOutputParameter("@jobType", SqlDbType.TinyInt);

                        if ((int)DbHelper.executeProcedureReturnValue("user_autoLogin",
                            new SqlParameter("@id", id),
                            new SqlParameter("@lastLoginTime", lastLoginTime),
                            new SqlParameter("@newLoginTime", newLoginTime),
                            new SqlParameter("@lastLoginIp", ConvertHelper.ipToNumber(HttpHelper.getIP())),
                            nameParam,
                            emailParam,
                            roleParam,
                            avatarParam,
                            skillsParam,
                            levelParam,
                            jobStateParam,
                            workYearTypeParam,
                            jobTypeParam) != 0) {
                            logout();
                            return null;
                        }

                        user = new User() {
                            id = id,
                            name = (string)nameParam.Value,
                            email = (string)emailParam.Value,
                            role = (UserRole)(byte)roleParam.Value,
                            avatar = (string)avatarParam.Value,
                            skills = (string)skillsParam.Value,
                            level = (int)levelParam.Value,
                            jobState = (JobState)(byte)jobStateParam.Value,
                            workYearType = (WorkYearType)(byte)workYearTypeParam.Value,
                            jobType = (JobType)(byte)jobTypeParam.Value,
                        };

                        onLogin(user, newLoginTime, true);

                    } catch { }

                }

            }

#if AUTO_LOGIN
            if (user == null) {
                currentUser = user = new User() {
                    id = 0,
                    role = UserRole.admin,
                    name = "<测试账号>",
                    email = "<测试账号邮箱>",
                    avatar = "<测试账号头像>",
                    skills = "",
                    level = 1,
                };
            }
#endif
            return user;
        }

        /// <summary>
        /// 确保用户为登录状态，否则直接向用户响应错误。
        /// </summary>
        public static User checkLogin() {

            // 已登录则返回。
            var user = getLoginUser();
            if (user == null) {
                HttpHelper.jsonp(CommonResponse.authError);
                HttpContext.Current.Response.End();
            }

            return user;

        }

        /// <summary>
        /// 确保用户为登录状态，同时检查权限。否则直接向用户响应错误。
        /// </summary>
        /// <param name="role">要求的用户最小权限。</param>
        public static User checkLogin(UserRole role) {
            User user = currentUser;
            if (user.role < role) {
                HttpHelper.jsonp(CommonResponse.unaccessableError);
                HttpContext.Current.Response.End();
            }
            return user;
        }

        /// <summary>
        /// 用户登录后统一回调。
        /// </summary>
        /// <param name="user">当前登录的用户信息。</param>
        /// <param name="newLoginTime">最后登录时间。</param>
        /// <param name="saveLoginCookie">指示是否将登录信息保存为 Cookie。</param>
        private static void onLogin(User user, DateTime newLoginTime, bool saveLoginCookie) {
            currentUser = user;

            // 保存登录信息。
            HttpHelper.setCookie(userIdCookieName, user.id.ToString(), cookieExpires).Domain = cookieDomain;
            HttpHelper.setCookie(userNameCookieName, user.name, cookieExpires).Domain = cookieDomain;

            // 保存自动登录信息。
            if (saveLoginCookie) {
                HttpHelper.setCookie(userPassportCookieName, Encryption.encryptAES(newLoginTime.Ticks.ToString()), cookieExpires).Domain = cookieDomain;
            }
        }

        /// <summary>
        /// 使用指定的账号和密码登录。
        /// </summary>
        /// <param name="accountName">登录的账号。可以使用用户名、邮箱或手机号登录。</param>
        /// <param name="password">登录的密码。</param>
        /// <param name="saveLoginCookie">指示是否将登录信息保存为 Cookie。</param>
        /// <returns>返回标准 JSON 字符串。</returns>
        public static string login(string accountName, string password, bool saveLoginCookie = false) {

            var idParam = DbHelper.createOutputParameter("@id", SqlDbType.BigInt);
            var newLoginTime = DateTime.Now;

            var nameParam = DbHelper.createOutputParameter("@name", SqlDbType.VarChar, 32);
            var emailParam = DbHelper.createOutputParameter("@email", SqlDbType.VarChar, 64);
            var roleParam = DbHelper.createOutputParameter("@role", SqlDbType.TinyInt);
            var avatarParam = DbHelper.createOutputParameter("@avatar", SqlDbType.VarChar, 256);
            var skillsParam = DbHelper.createOutputParameter("@skills", SqlDbType.VarChar, 256);
            var levelParam = DbHelper.createOutputParameter("@level", SqlDbType.Int);
            var jobStateParam = DbHelper.createOutputParameter("@jobState", SqlDbType.TinyInt);
            var workYearTypeParam = DbHelper.createOutputParameter("@workYearType", SqlDbType.TinyInt);
            var jobTypeParam = DbHelper.createOutputParameter("@jobType", SqlDbType.TinyInt);

            switch ((int)DbHelper.executeProcedureReturnValue("user_login",
                new SqlParameter("@accountName", accountName),
                new SqlParameter("@password", password),
                new SqlParameter("@newLoginTime", newLoginTime),
                new SqlParameter("@lastLoginIp", ConvertHelper.ipToNumber(HttpHelper.getIP())),
                idParam,
                nameParam,
                emailParam,
                roleParam,
                avatarParam,
                skillsParam,
                levelParam,
                jobStateParam,
                workYearTypeParam,
                jobTypeParam)) {

                // 用户登录成功。
                case 0:

                    onLogin(new User() {
                        id = (long)idParam.Value,
                        name = (string)nameParam.Value,
                        email = (string)emailParam.Value,
                        role = (UserRole)(byte)roleParam.Value,
                        avatar = (string)avatarParam.Value,
                        skills = (string)skillsParam.Value,
                        level = (int)levelParam.Value,
                        jobState = (JobState)(byte)jobStateParam.Value,
                        workYearType = (WorkYearType)(byte)workYearTypeParam.Value,
                        jobType = (JobType)(byte)jobTypeParam.Value,
                    }, newLoginTime, saveLoginCookie);

                    return CommonResponse.success;

                // 用户名不存在。
                case 1:
                    return "{\"code\":1,\"msg\":\"用户名或邮箱不存在\"}";

                // 密码错误。
                default:
                    return "{\"code\":2,\"msg\":\"用户名或密码错误\"}";

            }

        }

        /// <summary>
        /// 注销当前登录的用户。
        /// </summary>
        public static void logout() {
            HttpContext.Current.Session.Remove(userSessionName);
            HttpHelper.removeCookie(userPassportCookieName);
            HttpHelper.removeCookie(userIdCookieName);
            HttpHelper.removeCookie(userNameCookieName);
        }

        /// <summary>
        /// 使用指定的信息注册新用户。
        /// </summary>
        /// <param name="name">登录的邮箱。</param>
        /// <param name="password">登录的密码。</param>
        /// <returns>返回标准 JSON 字符串。</returns>
        public static string register(string name, string password) {

            if (String.IsNullOrEmpty(name)) {
                return "{\"code\":2,\"msg\":\"用户名不能为空\"}";
            }

            if (name.getAnsiLength() > 32) {
                return "{\"code\":3,\"msg\":\"用户名不能超过 32 个字符\"}";
            }

            if (String.IsNullOrEmpty(password)) {
                return "{\"code\":4,\"msg\":\"密码不能为空\"}";
            }

            if (password.getAnsiLength() > 32) {
                return "{\"code\":5,\"msg\":\"密码不能超过 32 个字符\"}";
            }

            long id = (long)DbHelper.executeProcedureScalar("user_register",
                new SqlParameter("@id", currentUser.id),
                new SqlParameter("@name", name),
                new SqlParameter("@password", password));

            if (id == 0) {
                return CommonResponse.updateError;
            }

            return CommonResponse.success;

        }

        /// <summary>
        /// 检查指定用户名是否被使用。
        /// </summary>
        /// <param name="name">要检查的用户名。</param>
        /// <returns>返回标准 JSON 字符串。</returns>
        public static string checkName(string name) {

            if (String.IsNullOrEmpty(name)) {
                return "{\"code\":2,\"msg\":\"用户名不能为空\"}";
            }

            if (name.getAnsiLength() > 32) {
                return "{\"code\":3,\"msg\":\"用户名不能超过 32 个字符\"}";
            }

            if (DbHelper.executeProcedureScalar("user_checkName",
                new SqlParameter("@name", name)) != null) {
                return "{\"code\":1,\"msg\":\"用户名已被使用\"}";
            }

            return "{\"code\":0,\"msg\":\"用户名可用\"}";
        }

        /// <summary>
        /// 检查指定用户名是否被使用。
        /// </summary>
        /// <param name="email">要检查的邮箱。</param>
        /// <returns>返回标准 JSON 字符串。</returns>
        public static string checkEmail(string email) {

            if (String.IsNullOrEmpty(email)) {
                return "{\"code\":2,\"msg\":\"邮箱不能为空\"}";
            }

            if (email.getAnsiLength() > 64) {
                return "{\"code\":3,\"msg\":\"邮箱不能超过 64 个字符\"}";
            }

            if (DbHelper.executeProcedureScalar("user_checkEmail",
                new SqlParameter("@email", email)) != null) {
                return "{\"code\":1,\"msg\":\"邮箱已被使用\"}";
            }

            return "{\"code\":0,\"msg\":\"邮箱可用\"}";
        }

        /// <summary>
        /// 更新用户的用户名。
        /// </summary>
        /// <param name="name">要更新的用户名。</param>
        /// <returns>返回标准 JSON 字符串。</returns>
        public static string updateName(string name) {

            if (String.IsNullOrEmpty(name)) {
                return "{\"code\":2,\"msg\":\"用户名不能为空\"}";
            }

            if (name.getAnsiLength() > 32) {
                return "{\"code\":3,\"msg\":\"用户名不能超过 32 个字符\"}";
            }

            var newLoginTime = DateTime.Now;

            if ((int)DbHelper.executeProcedureReturnValue("user_updateName",
                new SqlParameter("@id", currentUser.id),
                new SqlParameter("@name", name),
                new SqlParameter("@newLoginTime", newLoginTime)) != 0) {
                    return "{\"code\":1,\"msg\":\"用户名“" + EncodeHelper.encodeJavaScriptString(name) + "”已被使用，不能重复使用\"}";
            }

            onLogin(currentUser, newLoginTime, true);
            return CommonResponse.success;
        }

        /// <summary>
        /// 更新用户的邮箱。
        /// </summary>
        /// <param name="email">要更新的邮箱。</param>
        /// <returns>返回标准 JSON 字符串。</returns>
        public static string updateEmail(string email) {

            if (String.IsNullOrEmpty(email)) {
                return "{\"code\":2,\"msg\":\"邮箱不能为空\"}";
            }

            if (email.getAnsiLength() > 64) {
                return "{\"code\":3,\"msg\":\"邮箱不能超过 64 个字符\"}";
            }

            int authCode = RandHelper.nextInt();

            try {
                MailHelper.sendEmail(email, email, "验证邮件", "验证内容");
            } catch {
                return "{\"code\":4,\"msg\":\"无法发送邮件到邮箱“" + EncodeHelper.encodeJavaScriptString(email) + "”，请确认邮箱是否正确\"}";
            }

            if ((int)DbHelper.executeProcedureReturnValue("user_updateEmail",
                new SqlParameter("@id", currentUser.id),
                new SqlParameter("@email", email),
                new SqlParameter("@authCode", authCode)) != 0) {
                return "{\"code\":1,\"msg\":\"邮箱“" + EncodeHelper.encodeJavaScriptString(email) + "”已被使用，不能重复使用\"}";
            }

            currentUser.email = email;

            return CommonResponse.success;
        }

        /// <summary>
        /// 验证用户的邮箱。
        /// </summary>
        /// <param name="userId">要验证的用户序号。</param>
        /// <param name="checkCode">用户的验证码。</param>
        /// <returns>返回标准 JSON 字符串。</returns>
        public static string authEmail(long userId, int authCode) {

            if (authCode < 0 || (int)DbHelper.executeProcedureReturnValue("user_authEmail",
                new SqlParameter("@id", userId),
                new SqlParameter("@authCode", authCode)) != 0) {
                return "{\"code\":1,\"msg\":\"验证失败\"}";
            }

            return CommonResponse.success;
        }

        /// <summary>
        /// 更改当前登录用户的密码。
        /// </summary>
        /// <param name="oldPassword">用户的原始密码。</param>
        /// <param name="newPassword">用户的新密码。</param>
        /// <returns>返回标准 JSON 字符串。</returns>
        public static string changePassword(string oldPassword, string newPassword) {

            if (String.IsNullOrEmpty(newPassword)) {
                return "{\"code\":2,\"msg\":\"新密码不能为空\"}";
            }

            if (newPassword.getAnsiLength() > 32) {
                return "{\"code\":3,\"msg\":\"密码不能超过 32 个字符\"}";
            }

            var newLoginTime = DateTime.Now;

            if (DbHelper.executeProcedureNonQuery("user_changePassword",
                new SqlParameter("@id", currentUser.id),
                new SqlParameter("@newLoginTime", newLoginTime),
                new SqlParameter("@oldPassword", oldPassword),
                new SqlParameter("@newPassword", newPassword)) == 0) {
                return "{\"code\":1,\"msg\":\"原密码错误\"}";
            }

            onLogin(currentUser, newLoginTime, true);
            return CommonResponse.success;
        }

        /// <summary>
        /// 更新用户头像。
        /// </summary>
        /// <param name="avatar">要更新的头像地址。</param>
        /// <returns>返回标准 JSON 字符串。</returns>
        public static string updateAvatar(string avatar) {

            if (DbHelper.executeProcedureNonQuery("user_updateAvatar",
                new SqlParameter("@id", currentUser.id),
                new SqlParameter("@avatar", avatar)) == 0) {
                return CommonResponse.updateError;
            }

            currentUser.avatar = avatar;

            return CommonResponse.success;
        }

        /// <summary>
        /// 获取当前用户的信息数。
        /// </summary>
        /// <returns>返回标准 JSON 字符串。</returns>
        public static string getMsgCount() {
            return "{\"code\":0,\"value\":" + DbHelper.executeProcedureScalar("user_getMsgCount", new SqlParameter("@id", currentUser.id)) + "}";
        }

        /// <summary>
        /// 设置用户的角色。
        /// </summary>
        /// <param name="userId">要设置的用户序号。</param>
        /// <param name="role">设置的用户角色。</param>
        /// <returns>返回标准 JSON 字符串。</returns>
        public static string updateRole(long userId, UserRole role) {

            if (currentUser.id == userId) {
                return "{\"code\":2,\"msg\":\"无法设置自身的权限\"}";
            }

            if (currentUser.role != UserRole.admin) {
                return CommonResponse.authError;
            }

            if (DbHelper.executeProcedureNonQuery("user_updateRole",
                new SqlParameter("@id", userId),
                new SqlParameter("@role", role)) == 0) {
                return CommonResponse.updateError;
            }

            return CommonResponse.success;
        }

        public static object getUserInfo(long userId) {
            return null;
        }

        /// <summary>
        /// 更新用户的信息。
        /// </summary>
        /// <param name="title">用户头衔。</param>
        /// <param name="signature">用户签名。</param>
        /// <param name="description">用户描述。</param>
        /// <param name="skills">用户技能。</param>
        /// <param name="address">用户地址。</param>
        /// <param name="contact">用户联系方式。</param>
        /// <returns>返回标准 JSON 字符串。</returns>
        public static string updateUserInfo(string avatar, string title, string signature, string description, string skills, string address, string contact) {
            return CommonResponse.success;
        }

    }

    /// <summary>
    /// 表示一个已登录的用户信息。
    /// </summary>
    public sealed class User {

        /// <summary>
        /// 获取当前用户的 ID。
        /// </summary>
        public long id;

        /// <summary>
        /// 获取当前用户的名字。
        /// </summary>
        public string name;

        /// <summary>
        /// 获取当前用户的邮箱。
        /// </summary>
        public string email;

        /// <summary>
        /// 获取当前用户的角色。
        /// </summary>
        public UserRole role;

        /// <summary>
        /// 获取当前用户的头像地址。
        /// </summary>
        public string avatar;

        /// <summary>
        /// 获取当前用户的擅长领域。
        /// </summary>
        public string skills;

        /// <summary>
        /// 获取当前用户的等级。
        /// </summary>
        public int level;

        /// <summary>
        /// 获取当前用户的签名。
        /// </summary>
        public string signature;

        /// <summary>
        /// 获取当前用户的工作状态。
        /// </summary>
        public JobState jobState;

        /// <summary>
        /// 获取当前用户的工作类型。
        /// </summary>
        public JobType jobType;

        /// <summary>
        /// 获取当前用户的工作年限。
        /// </summary>
        public WorkYearType workYearType;

    }

    /// <summary>
    /// 表示用户角色。
    /// </summary>
    public enum UserRole {

        /// <summary>
        /// 被封锁的账号。
        /// </summary>
        deniedUser = 0,

        /// <summary>
        /// 普通用户。
        /// </summary>
        standard = 1,

        /// <summary>
        /// 网站编辑。
        /// </summary>
        vip = 2,

        /// <summary>
        /// 网站编辑。
        /// </summary>
        editor = 3,

        /// <summary>
        /// 管理员。
        /// </summary>
        admin = 4,

    }

    /// <summary>
    /// 表示用户的工作类型。
    /// </summary>
    public enum JobType : byte {

        /// <summary>
        /// 其它。
        /// </summary>
        other,

        /// <summary>
        /// 技术负责人/总监。
        /// </summary>
        cto,

        /// <summary>
        /// 部门经理。
        /// </summary>
        manager,

        /// <summary>
        /// 项目经理。
        /// </summary>
        pjm,

        /// <summary>
        /// 运维。
        /// </summary>
        dm,

        /// <summary>
        /// 产品。
        /// </summary>
        pd,

        /// <summary>
        /// 视觉/交互。
        /// </summary>
        designer,

        /// <summary>
        /// 开发。
        /// </summary>
        developer,

        /// <summary>
        /// 构架师/安全工程师。
        /// </summary>
        architect,

        /// <summary>
        /// 测试。
        /// </summary>
        qa,

        /// <summary>
        /// 数据库管理员。
        /// </summary>
        dba,

        /// <summary>
        /// 运营/网站编辑。
        /// </summary>
        editor,

        /// <summary>
        /// 个人站长。
        /// </summary>
        siteOwner,

        /// <summary>
        /// HR/人事招聘
        /// </summary>
        hr,

        /// <summary>
        /// 其它 IT 技术。
        /// </summary>
        it,

        /// <summary>
        /// 非 IT 技术。
        /// </summary>
        nonIT,

    }

    /// <summary>
    /// 表示工作状态。
    /// </summary>
    public enum JobState : byte {

        /// <summary>
        /// 保密。
        /// </summary>
        unknown,

        /// <summary>
        /// 在校学生。
        /// </summary>
        student,

        /// <summary>
        /// 实习中。
        /// </summary>
        practice,

        /// <summary>
        /// 就职。
        /// </summary>
        working,

        /// <summary>
        /// 待跳槽。
        /// </summary>
        finding,

        /// <summary>
        /// 找工作中。
        /// </summary>
        waiting,

        /// <summary>
        /// 退休。
        /// </summary>
        retire,

    }

    /// <summary>
    /// 表示工作年限。
    /// </summary>
    public enum WorkYearType: byte {

        /// <summary>
        /// 保密。
        /// </summary>
        unknown,
        
        /// <summary>
        /// 在校学生。
        /// </summary>
        student,

        /// <summary>
        /// 应届毕业生。
        /// </summary>
        graduate,

        /// <summary>
        /// 一年以上工作经验。
        /// </summary>
        year1,

        /// <summary>
        /// 两年以上工作经验。
        /// </summary>
        year2,

        /// <summary>
        /// 三年以上工作经验。
        /// </summary>
        year3,

        /// <summary>
        /// 五年以上工作经验。
        /// </summary>
        year5,

        /// <summary>
        /// 十年以上工作经验。
        /// </summary>
        year10,

    }

}
